grammar logo;

options {
  language = Java;
  output = AST;
  //backtrack = true;
}

@header {
  package lexparser;
  import ast.*;
  import ast.interfaces.*;
  import java.util.LinkedList;
}

@lexer::header{
  package lexparser;
  import ast.*;
  import ast.interfaces.*;
  import java.util.LinkedList;
}

start : prog
      ;

prog       returns [IAST e]
           : '[' op=cmds? ']'
              {
                IASTprog ast = new ASTprog($op.e);
                $e = ast;
              }
           ;

cmds       returns [LinkedList<IAST> e]
           : op1=cmd  {             
			                LinkedList<IAST> a = new LinkedList<IAST>();
			                a.add($op1.e);
			                $e = a;
                     }
              (
                SEP op2=cmds {
                      LinkedList<IAST> b = $e;
                      b.addAll($op2.e);
                      $e = b;
                             }
              )*
           | op3=dec  {             
                      LinkedList<IAST> a = new LinkedList<IAST>();
                      a.add($op3.e);
                      $e = a;
                     }
              (
                SEP op4=cmds {
                      LinkedList<IAST> b = $e;
                      b.addAll($op4.e);
                      $e = b;
                             }
              )*
           ;

cmd        returns [IAST e]
           : 'PRINT' op=exp
              {
                IASTprint ast = new ASTprint($op.e);
                $e = ast;
              }
           | 'MOVE' op=exp
              {
                IASTmove ast = new ASTmove($op.e);
                $e = ast;
              }
           | 'TURN' op=exp
              {
                IASTturn ast = new ASTturn($op.e);
                $e = ast;
              }
           | '(' 'PROCCALL' op5=IDENT op6=exps ')'
              {
                IASTproccall ast = new ASTproccall($op5.text,$op6.e);
                $e = ast;
              }
           | 'SET' op1=IDENT op2=exp
              {
                IASTset ast = new ASTset($op1.text,$op2.e);
                $e = ast;
              }
           | 'IF' op2=exp op3=prog op4=prog
              {
                IASTif ast = new ASTif($op2.e,$op3.e,$op4.e);
                $e = ast;
              }
           | 'WHILE' op2=exp op3=prog
              {
                IASTwhile ast = new ASTwhile($op2.e,$op3.e);
                $e = ast;
              }
           | ('TRY'|'CATCH') prog IDENT prog
           | 'THOW' IDENT
           ;

dec        returns [IAST e]
           : 'PROC' op6=IDENT op7=idents '=' op8=prog
                    {
                      IASTproc ast = new ASTproc($op6.text,$op7.e,$op8.e);
                      $e = ast;
                    }
           | 'PROCREC' op6=IDENT op7=idents '=' op8=prog
                    {
                      IASTprocrec ast = new ASTprocrec($op6.text,$op7.e,$op8.e);
                      $e = ast;
                    }
           | 'FUN' op1=IDENT op2=idents '=' op3=exp
                    {
                      IASTfun ast = new ASTfun($op1.text,$op2.e,$op3.e);
                      $e = ast;
                    }
           | 'FUNREC' op1=IDENT op2=idents '=' op3=exp
                    {
                      IASTfunrec ast = new ASTfunrec($op1.text,$op2.e,$op3.e);
                      $e = ast;
                    }
           | 'VAR' op1=IDENT
                    {
                      IASTvar ast = new ASTvar($op1.text);
                      $e = ast;
                    }
           ;
           
idents     returns [IAST e]
           : op1=IDENT
              {
                LinkedList<String> a = new LinkedList<String>();
                a.add($op1.text);
                IASTidents ast = new ASTidents(a);
                $e = ast;
              }
           | op2=IDENT op3=idents
              {
                ASTidents a = (ASTidents) $op3.e;
                a.getIdents().add($op2.text);
                $e = a;
              }
           ;

term       returns [IAST e]
           : op=IDENT 
              {
                IASTident ast = new ASTident($op.text);
                $e = ast;
              }
           | op=NUM
              {
                IASTnum ast = new ASTnum(Integer.parseInt($op.text));
                $e = ast;
              }
           | 'IFF' op1=exp '(' op5=exp ')' '(' op4=exp ')'
              {
                IASTiff ast = new ASTiff($op1.e,$op5.e,$op4.e);
                $e = ast;
              }
           | '(' op1=exp ')'
              {
                IASTexp ast = new ASTexp($op1.e);
                $e = ast;
              }
           | '(' 'FUNCALL' op2=IDENT op3=exps ')'
              {
                IASTfuncall ast = new ASTfuncall($op2.text,$op3.e);
                $e = ast;
              }
           | op=(TRUE|FALSE)
              {
                IASTbool ast = new ASTbool(Boolean.parseBoolean($op.text));
                $e = ast;              
              }
           ;

negation   returns [IAST e]
           : NOT* op1=term
              { 
//                IASTnot ast= new ASTnot($op1.e);
//                $e = ast;
                  $e = $op1.e;
              }
           ;

mult       returns [IAST e]
           : op1=negation { $e = $op1.e; }
             (   
                 DIV op2=negation  { $e = new ASTdiv($e,$op2.e); }
               | MULT op2=negation { $e = new ASTmult($e,$op2.e); }
             )*
           ;
  
add        returns [IAST e]
           : op3=mult {$e = $op3.e;}
              (
                  PLUS op2=mult { $e = new ASTplus($e,$op2.e); }
                | MINUS op2=mult { $e = new ASTminus($e,$op2.e); }
              )*
           ;

relation   returns [IAST e]
           : op1=add {$e = $op1.e;}
              (
                  LT op2=add {$e = new ASTlt($e,$op2.e);}
                | GT op2=add {$e = new ASTgt($e,$op2.e);}
                | EQ op2=add {$e = new ASTeq($e,$op2.e);}
              )*
           ;

exp        returns [IAST e]
           : op1=relation {$e = $op1.e;} 
              (
                  AND relation
                | OR relation
              )*
           ;
           
exps       returns [LinkedList<IAST> e]
           : op1=exp
              {
                LinkedList<IAST> a = new LinkedList<IAST>();
                a.add($op1.e);
                $e = a;
              }
            (op2=exps
              {
                LinkedList<IAST> b = $e;
                 b.addAll($op2.e);
                 $e = b;
              }
            )?
           ;
           
LT         : '<';
GT         : '>';
EQ         : '=';
PLUS       : '+';
MINUS      : '-';
MULT       : '*';
DIV        : '/';
NOT        : 'not';
AND        : 'and';
OR         : 'or';
TRUE       : 'true' | 'TRUE';
FALSE      : 'false' | 'FALSE';
NUM        : '0'..'9'+;   
SEP        : ';';     
IDENT      : ('a'..'z'|'A'..'Z'|'0'..'9'|'_')+;
WS         : (' '|'\t'|'\n'|'\r'|'\f')+ { $channel = HIDDEN; };           